//  Based in part on the AudioManager component found on the XNA wiki.  No author listed.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Media;

namespace YEngineLib.Audio
{
    public sealed class YAudioManager
    {
        public YSFX SFX;
        public YMusic Music;

        //  This is shared between the YSFX and YMusic classes
        private ContentManager content;
        
        /// <summary>
        /// Creates a new Audio Manager. Add this to the Components collection of your Game.
        /// </summary>
        /// <param name="game">The Game</param>
        public void Initialize(Game game)
        {
            //  Create our own Content Manager
            content = new ContentManager(game.Content.ServiceProvider, game.Content.RootDirectory);
            
            //  Point SFX and Music to use our "personal" content manager
            SFX = new YSFX(content);
            Music = new YMusic(content);
        }
        
        /// <summary>
        /// Unloads all loaded songs and sounds.
        /// </summary>
        public void UnloadContent()
        {
            content.Unload();
        }
    }

    /// <summary>
    /// Purely deals with Loading and Playing Sounds.
    /// </summary>
    public class YSFX
    {
        #region Members
        ContentManager content;

        Dictionary<string, SoundEffect> sounds = new Dictionary<string, SoundEffect>();
        #endregion

        #region Properties

        /// <summary>
        /// Set this to true if SFX should be played.
        /// </summary>
        public bool Enabled { get; set; }

        /// <summary>
        /// Gets or sets the master volume for all sounds. 1.0f is max volume.
        /// </summary>
        public float MasterVolume
        {
            get { return SoundEffect.MasterVolume; }
            set { SoundEffect.MasterVolume = value; }
        }
        #endregion

        public YSFX(ContentManager content)
        {
            this.content = content;
            Enabled = true;
        }

        /// <summary>
        /// Loads a SoundEffect. Will just do nothing if sound already loaded.
        /// </summary>
        /// <param name="soundName">Name of the sound to load</param>
        /// <param name="soundPath">Path to the sound asset file (relative to Content directory)</param>
        public void LoadSound(string soundName, string soundPath)
        {
            if (!sounds.ContainsKey(soundName.ToLower()))
            {
                //throw new InvalidOperationException(string.Format("Sound '{0}' has already been loaded", soundName));
                sounds.Add(soundName.ToLower(), content.Load<SoundEffect>(soundPath));
            }
        }

        /// <summary>
        /// Plays the sound of the given name.
        /// </summary>
        /// <param name="soundName">Name of the sound</param>
        public void PlaySound(string soundName)
        {
            PlaySound(soundName, 1.0f, 0.0f, 0.0f);
        }

        /// <summary>
        /// Plays the sound of the given name at the given volume.
        /// </summary>
        /// <param name="soundName">Name of the sound</param>
        /// <param name="volume">Volume, 0.0f to 1.0f</param>
        public void PlaySound(string soundName, float volume)
        {
            PlaySound(soundName, volume, 0.0f, 0.0f);
        }

        /// <summary>
        /// Plays the sound of the given name with the given parameters.
        /// </summary>
        /// <param name="soundName">Name of the sound</param>
        /// <param name="volume">Volume, 0.0f to 1.0f</param>
        /// <param name="pitch">Pitch, -1.0f (down one octave) to 1.0f (up one octave)</param>
        /// <param name="pan">Pan, -1.0f (full left) to 1.0f (full right)</param>
        public void PlaySound(string soundName, float volume, float pitch, float pan)
        {
            if (!Enabled)
                return;

            SoundEffect sound;

            if (!sounds.TryGetValue(soundName.ToLower(), out sound))
            {
                throw new ArgumentException(string.Format("Sound '{0}' not found", soundName));
            }

            sound.Play(volume, pitch, pan);
        }
    }

    public class YMusic
    {
        #region Members
        private ContentManager content;

        private Dictionary<string, Song> songs = new Dictionary<string, Song>();

        private Song currentSong = null;

        private bool isMusicPaused = false;

        private bool isEnabled = true;
        #endregion

        #region Properties

        /// <summary>
        /// Set to true if Music should be played.
        /// </summary>
        public bool Enabled 
        { 
            get { return isEnabled; }
            set 
            { 
                isEnabled = value;

                if (isEnabled == false)
                    Pause();
                else
                    Resume();
            } 
        }

        /// <summary>
        /// Returns true if the game can play its music,
        /// false if the user is playing their own custom music.
        /// </summary>
        public bool GameHasControl { get { return MediaPlayer.GameHasControl; } }

        /// <summary>
        /// Gets the name of the currently playing song, or null if no song is playing.
        /// </summary>
        public string CurrentSong { get; private set; }

        /// <summary>
        /// Gets or sets the volume to play songs. 1.0f is max volume.
        /// </summary>
        public float MusicVolume
        {
            get { return MediaPlayer.Volume; }
            set { MediaPlayer.Volume = value; }
        }
        #endregion

        public YMusic(ContentManager content)
        {
            this.content = content;

            if (GameHasControl == false)
                Enabled = false;
            else
                Enabled = true;
        }

        /// <summary>
        /// Loads a Song.
        /// </summary>
        /// <param name="songName">Name of the song to load</param>
        public void LoadSong(string songName)
        {
            LoadSong(songName, songName);
        }

        /// <summary>
        /// Loads a Song.  Will just do nothing if song already loaded.
        /// </summary>
        /// <param name="songName">Name of the song to load</param>
        /// <param name="songPath">Path to the song asset file</param>
        public void LoadSong(string songName, string songPath)
        {
            if (!songs.ContainsKey(songName))
            {
                songs.Add(songName, content.Load<Song>(songPath));
//                throw new InvalidOperationException(string.Format("Song '{0}' has already been loaded", songName));
            }
        }

        /// <summary>
        /// Starts playing the song with the given name. If it is already playing, this method
        /// does nothing. If another song is currently playing, it is stopped first.
        /// </summary>
        /// <param name="songName">Name of the song to play</param>
        public void PlaySong(string songName)
        {
            PlaySong(songName, false);
        }

        /// <summary>
        /// Starts playing the song with the given name. If it is already playing, this method
        /// does nothing. If another song is currently playing, it is stopped first.
        /// </summary>
        /// <param name="songName">Name of the song to play</param>
        /// <param name="loop">True if song should loop, false otherwise</param>
        public void PlaySong(string songName, bool loop)
        {
            //if (CurrentSong != songName)
            {
                if (currentSong != null)
                {
                    MediaPlayer.Stop();
                }

                if (!songs.TryGetValue(songName, out currentSong))
                {
                    throw new ArgumentException(string.Format("Song '{0}' not found", songName));
                }

                CurrentSong = songName;

                isMusicPaused = false;
                MediaPlayer.IsRepeating = loop;
                MediaPlayer.Play(currentSong);

                if (!Enabled)
                {
                    MediaPlayer.Pause();
                }
            }
        }

        /// <summary>
        /// Pauses the currently playing song.
        /// </summary>
        public void Pause()
        {
            if (currentSong != null && !isMusicPaused)
            {
                //if (Enabled) 
                MediaPlayer.Pause();
                isMusicPaused = true;
            }
        }

        /// <summary>
        /// Continues playing the song from the point where it was Paused.
        /// </summary>
        public void Resume()
        {
            if (currentSong != null && isMusicPaused)
            {
                if (Enabled) 
                    MediaPlayer.Resume();
                isMusicPaused = false;
            }
        }

        /// <summary>
        /// Stops the song and resets it back to the beginning.
        /// </summary>
        public void Stop()
        {
            if (currentSong != null && MediaPlayer.State != MediaState.Stopped)
            {
                MediaPlayer.Stop();
                isMusicPaused = false;
            }
        }
    }
}
